﻿using System;
using System.Collections.Generic;
using System.ComponentModel;

using System.Linq;
using System.Text;
using System.Data;
using System.Text.RegularExpressions;
using System.IO;
using System.Globalization;

using System.Windows;

namespace SharpFileZillaStats {
    public class StatsReader : INotifyPropertyChanged {
        private DataSet dataSet;
        private DataView dataView;
        private DataTable dataTable;
        private List<FileInfo> files = new List<FileInfo>();
        private Regex rLine       = new Regex("> RETR");
        private Regex rLineIsFail = new Regex("> 550");
        private Regex rID = new Regex(@"(?<=^\()\d{6}");
        private Regex rDTG = new Regex(@"(?<=^\(\d{6}\) )\d{2}.\d{2}.\d{4} \d{2}:\d{2}:\d{2}");
        private Regex rUser = new Regex(@"(?<=:\d{2} - ).+(?= \(\d{1,3}\.)");
        private Regex rFile = new Regex(@"(?<=RETR ).*$");
        private Regex rIP = new Regex(@"(?<= \()\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(?=\)>)");

        private const String cID = "ID";
        private const String cIP = "IP";
        private const String cLogin = "Login";
        private const String cDTG = "DTG";
        private const String cFile = "File";


        public String ColumnID { get { return cID; } }
        public String ColumnIP { get { return cIP; } }
        public String ColumnLogin { get { return cLogin; } }
        public String ColumnDTG   { get { return cDTG; } }
        public String ColumnFile { get { return cFile; } }

        public String ColumnCountry { get { return cCountry; } }
        public String ColumnRegion { get { return cRegion; } }
        public String ColumnCity { get { return cCity; } }
        public String ColumnLatitude { get { return cLatitude; } }
        public String ColumnLongitude { get { return cLongitude; } }
        
        private const String cCountry = "Country";
        private const String cRegion = "Region";
        private const String cCity = "City";
        private const String cLatitude = "Latitude";
        private const String cLongitude = "Longitude";

        private ObservableSortedSet<String> hsIPs = new ObservableSortedSet<String>();
        private ObservableSortedSet<String> hsLogins = new ObservableSortedSet<String>();
        private ObservableSortedSet<String> hsFiles = new ObservableSortedSet<String>();
        //private DataView dataview;

        private String dataViewInfo = "no logfiles added";
        private LookupService LookupService;
        private bool useLookupService = false;
        public StatsReader() {
            
            this.dataSet = new DataSet();
            this.dataTable = new DataTable("table");
            dataTable.Columns.Add(cID, typeof(long));
            dataTable.Columns.Add(cDTG, typeof(DateTime));
            dataTable.Columns.Add(cLogin, typeof(String));
            dataTable.Columns.Add(cIP, typeof(String));
            dataTable.Columns.Add(cFile, typeof(String));

            dataTable.PrimaryKey = new DataColumn[] { dataTable.Columns[cDTG]
                                                    , dataTable.Columns[cID]
                                                    , dataTable.Columns[cFile]
                                                    };

            string dbPath = (Path.IsPathRooted(Settings.Default.GeoCityDatabase)) ? 
                Settings.Default.GeoCityDatabase : 
                Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Settings.Default.GeoCityDatabase);
            this.useLookupService = File.Exists(dbPath);
            if(useLookupService) {
                this.LookupService = new LookupService(Settings.Default.GeoCityDatabase);
                dataTable.Columns.Add(cCountry, typeof(String));
                dataTable.Columns.Add(cRegion, typeof(String));
                dataTable.Columns.Add(cCity, typeof(String));
                dataTable.Columns.Add(cLatitude, typeof(double));
                dataTable.Columns.Add(cLongitude, typeof(double));
               
            }

            this.dataView = new DataView(this.dataTable); 

        }

        public void doTestAndDebugStuff(){
            this.hsLogins.Add("TESTUSEREEEEEEE");
        }

        public DataView DataView { get { return this.dataView; } }
        public String DataViewInfo { get { return this.dataViewInfo; } }

        public ObservableSortedSet<String> IPs { get { return this.hsIPs; } }
        public ObservableSortedSet<String> Files { get { return this.hsFiles; } }
        public ObservableSortedSet<String> LoginRoles { get { return this.hsLogins; } }

        public void setFilters(Regex IPFilter, Regex FileFilter, Regex LoginRoleFilter) {
            
            if(IPFilter == null) IPFilter = new Regex(".*");
            if(LoginRoleFilter == null) LoginRoleFilter = new Regex(".*");
            if(FileFilter == null) FileFilter = new Regex(".*");

            //TODO
            EnumerableRowCollection<DataRow> rows = from matchingItem in dataTable.AsEnumerable()
                          where IPFilter.IsMatch(matchingItem.Field<String>(cIP)) &&
                                LoginRoleFilter.IsMatch(matchingItem.Field<String>(cLogin)) &&
                                FileFilter.IsMatch(matchingItem.Field<String>(cFile))
                          select matchingItem;

            this.dataView = rows.AsDataView();
            int cntIPs      = rows.Select(v => v.Field<String>(cIP)).Distinct().Count();
            int cntLogins   = rows.Select(v => v.Field<String>(cLogin)).Distinct().Count();
            
            this.dataViewInfo = this.getDataViewFilterStats(this.dataView);
            if(this.PropertyChanged != null) {
                this.PropertyChanged(this, new PropertyChangedEventArgs("DataView"));
                this.PropertyChanged(this, new PropertyChangedEventArgs("DataViewInfo"));
            }
        }


        private String getDataViewFilterStats() {
            return this.getDataViewFilterStats(this.dataView);
        }
        private String getDataViewFilterStats(DataView dv) {
        
            int cntIPs    = (from DataRowView drv in dv select drv.Row.Field<String>(cIP)).Distinct().Count();
            int cntLogins = (from DataRowView drv in dv select drv.Row.Field<String>(cLogin)).Distinct().Count();
            int cntFiles  = (from DataRowView drv in dv select drv.Row.Field<String>(cFile)).Distinct().Count();


            String s = String.Format("Downloads {0}/{1}  "
                    +"IPs {2}/{3}  "
                    +"Login Roles {4}/{5}  "
                    +"Files {6}/{7}"
                , dv.Count, dv.Table.Rows.Count
                , cntIPs, this.hsIPs.Count
                , cntLogins, this.hsLogins.Count
                , cntFiles, this.hsFiles.Count
                );
            return s;
        }


        public void readFiles(String[] files) {
            this.readFiles((from s in files select new FileInfo(s)).AsEnumerable());
        }

        public void readFiles(IEnumerable<FileInfo> fileInfos) {
           
            foreach(FileInfo fileInfo in fileInfos) {
                if(fileInfo.Exists) {
                    this.files.Add(fileInfo);
                    int count = 0;

                    StreamReader sr = new StreamReader(fileInfo.FullName, true);
                    String line = null;
                    String nextLine = null;
                    DataTable dt_temp = this.dataTable.Clone();

                    while(!sr.EndOfStream) {
                        line = nextLine;
                        nextLine = sr.ReadLine();
                        try {
                            if(line != null && rLine.IsMatch(line)
                                //    && !rLineIsFail.IsMatch(nextLine)
                                ) {
                                long ID = long.Parse(rID.Match(line).Value);
                                DateTime DTG = DateTime.ParseExact(rDTG.Match(line).Value, "dd.MM.yyyy HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None);

                                String user = rUser.Match(line).Value;
                                String file = rFile.Match(line).Value;
                                file = Regex.Replace(file, "^/", "");
                                file = Regex.Replace(file, "/$", "");
                                String IP = System.Net.IPAddress.Parse(rIP.Match(line).Value).ToString();

                                this.hsIPs.Add(IP);
                                this.hsLogins.Add(user);
                                this.hsFiles.Add(file);

                                DataRow newRow = dt_temp.NewRow();
                                newRow[cID] = ID;
                                newRow[cDTG] = DTG;
                                newRow[cLogin] = user;
                                newRow[cIP] = IP;
                                newRow[cFile] = file;



                                if(useLookupService) {
                                    Location location = this.LookupService.getLocation(IP);
                                    newRow[cCountry] = location.countryName;
                                    newRow[cRegion] = location.regionName;
                                    newRow[cCity] = location.city;
                                    newRow[cLongitude] = location.longitude;
                                    newRow[cLatitude] = location.latitude;
                                }


                                dt_temp.Rows.Add(newRow);
                                count++;
                            }



                        } catch(Exception ex) {
                            Console.WriteLine(ex.Message);
                        }
                    }
                    sr.Close();
                    this.dataTable.Merge(dt_temp, true, MissingSchemaAction.Error);
                    this.dataViewInfo = String.Format("{0}; {1} new lines read from {2}"
                        , this.getDataViewFilterStats(), fileInfo.Name, this.dataTable.Rows.Count);
                }

            }

            if(this.PropertyChanged!= null) {
                this.PropertyChanged(this, new PropertyChangedEventArgs("LoginRoles"));
                this.PropertyChanged(this, new PropertyChangedEventArgs("Files"));
                this.PropertyChanged(this, new PropertyChangedEventArgs("DataViewBindingInfo"));
            }
           
        }
        public void readFile(FileInfo fileInfo) {
            this.readFiles(new FileInfo[] { fileInfo });
        }


        public event PropertyChangedEventHandler PropertyChanged;
    }
}
